<template><div><h2 id="sessions-和-cookies" tabindex="-1"><a class="header-anchor" href="#sessions-和-cookies"><span>Sessions 和 Cookies</span></a></h2>
<p>Sessions 和 cookies 允许数据在多次请求中保持，前者一般存储在服务端，后者存储在客户端浏览器中。在原生 PHP 中，可以分别使用全局变量 <code v-pre>$_SESSION</code> 和 <code v-pre>$_COOKIE</code> 来访问，Yii 将 session 和 cookie 封装成对象并增加一些功能，可通过面向对象方式访问它们。</p>
<h2 id="sessions" tabindex="-1"><a class="header-anchor" href="#sessions"><span>Sessions</span></a></h2>
<p>和 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/runtime-requests.md" target="_blank" rel="noopener noreferrer">请求</a> 和 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/runtime-responses.md" target="_blank" rel="noopener noreferrer">响应</a> 类似，默认可通过为 <code v-pre>yii\web\Session</code> 实例的 <code v-pre>session</code> <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-application-components.md" target="_blank" rel="noopener noreferrer">应用组件</a> 来访问 sessions。</p>
<h3 id="开启和关闭-sessions" tabindex="-1"><a class="header-anchor" href="#开启和关闭-sessions"><span>开启和关闭 Sessions</span></a></h3>
<p>可使用以下代码来开启和关闭 session。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$session</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">session</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 检查session是否开启</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$session</span><span class="token operator">-></span><span class="token property">isActive</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 开启session</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 关闭session</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 销毁session中所有已注册的数据</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">destroy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>多次调用 <code v-pre>yii\web\Session::open()</code> 和 <code v-pre>yii\web\Session::close()</code> 方法并不会产生错误，因为方法内部会先检查 session 是否已经开启。</p>
<h3 id="访问-session-数据" tabindex="-1"><a class="header-anchor" href="#访问-session-数据"><span>访问 Session 数据</span></a></h3>
<p>可使用如下方式访问 session 中的数据：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$session</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">session</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 获取session中的变量值，以下用法是相同的：</span></span>
<span class="line"><span class="token variable">$language</span> <span class="token operator">=</span> <span class="token variable">$session</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$language</span> <span class="token operator">=</span> <span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$language</span> <span class="token operator">=</span> <span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$_SESSION</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$_SESSION</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span> <span class="token punctuation">:</span> <span class="token constant">null</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 设置一个session变量，以下用法是相同的：</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'en-US'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'en-US'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$_SESSION</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'en-US'</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 删除一个session变量，以下用法是相同的：</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">unset</span><span class="token punctuation">(</span><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">unset</span><span class="token punctuation">(</span><span class="token variable">$_SESSION</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 检查session变量是否已存在，以下用法是相同的：</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">has</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$_SESSION</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 遍历所有session变量，以下用法是相同的：</span></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$session</span> <span class="token keyword">as</span> <span class="token variable">$name</span> <span class="token operator">=></span> <span class="token variable">$value</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$_SESSION</span> <span class="token keyword">as</span> <span class="token variable">$name</span> <span class="token operator">=></span> <span class="token variable">$value</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Info: 当使用 <code v-pre>session</code> 组件访问 session 数据时候，如果 session 没有开启会自动开启， 这和通过 <code v-pre>$_SESSION</code> 不同，<code v-pre>$_SESSION</code> 要求先执行 <code v-pre>session_start()</code>。</p>
</blockquote>
<p>当 session 数据为数组时，<code v-pre>session</code> 组件会限制你直接修改数据中的单元项，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$session</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">session</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 如下代码不会生效</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'number'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'lifetime'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">3600</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 如下代码会生效：</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'number'</span> <span class="token operator">=></span> <span class="token number">5</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'lifetime'</span> <span class="token operator">=></span> <span class="token number">3600</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 如下代码也会生效：</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'lifetime'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>可使用以下任意一个变通方法来解决这个问题：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$session</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">session</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 直接使用$_SESSION (确保Yii::$app->session->open() 已经调用)</span></span>
<span class="line"><span class="token variable">$_SESSION</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'number'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$_SESSION</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'lifetime'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">3600</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 先获取session数据到一个数组，修改数组的值，然后保存数组到session中</span></span>
<span class="line"><span class="token variable">$captcha</span> <span class="token operator">=</span> <span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$captcha</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'number'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$captcha</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'lifetime'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">3600</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token variable">$captcha</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 使用ArrayObject 数组对象代替数组</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>ArrayObject</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token operator">...</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'number'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'lifetime'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">3600</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 使用带通用前缀的键来存储数组</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha.number'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$session</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'captcha.lifetime'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">3600</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>为更好的性能和可读性，推荐最后一种方案，也就是不用存储 session 变量为数组，而是将每个数组项变成有相同键前缀的 session 变量。</p>
<h3 id="自定义-session-存储" tabindex="-1"><a class="header-anchor" href="#自定义-session-存储"><span>自定义 Session 存储</span></a></h3>
<p><code v-pre>yii\web\Session</code> 类默认存储 session 数据为文件到服务器上，Yii 提供以下 session 类实现不同的 session 存储方式：</p>
<ul>
<li><code v-pre>yii\web\DbSession</code>：存储 session 数据在数据库表中；</li>
<li><code v-pre>yii\web\CacheSession</code>：存储 session 数据到缓存中，缓存和配置中的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/caching-data.md#cache-components" target="_blank" rel="noopener noreferrer">缓存组件</a> 相关；</li>
<li><code v-pre>yii\redis\Session</code>：存储 session 数据到 <a href="http://redis.io/" target="_blank" rel="noopener noreferrer">redis</a> 存储媒介中；</li>
<li><code v-pre>yii\mongodb\Session</code>：存储 session 数据到 <a href="https://www.mongodb.com/" target="_blank" rel="noopener noreferrer">MongoDB</a>。</li>
</ul>
<p>所有这些 session 类支持相同的 API 方法集，因此，切换到不同的 session 存储介质不需要修改项目使用 session 的代码。</p>
<blockquote>
<p>Note: 如果通过 <code v-pre>$_SESSION</code> 访问使用自定义存储介质的 session，需要确保 session 已经用 [[yii\web\Session::open()]] 开启，这是因为在该方法中注册自定义 session 存储处理器。</p>
</blockquote>
<blockquote>
<p>Note: 如果使用自定义会话存储，则可能需要显式配置会话垃圾收集器。一些安装的 PHP（例如 Debian）使用垃圾收集器概率为 0，并在计划任务中离线清理会话文件。此过程不适用于您的自定义存储，因此您需要配置 [[yii\web\Session::$GCProbability]] 以使用非零值。</p>
</blockquote>
<p>学习如何配置和使用这些组件类请参考它们的 API 文档，如下为一个示例，显示如何在应用配置中配置 <code v-pre>yii\web\DbSession</code> 将数据表作为 session 存储介质。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'components'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'session'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'yii\web\DbSession'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token comment">// 'db' => 'mydb',  // 数据库连接的应用组件ID，默认为'db'.</span></span>
<span class="line">            <span class="token comment">// 'sessionTable' => 'my_session', // session 数据表名，默认为'session'.</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>也需要创建如下数据库表来存储 session 数据：</p>
<div class="language-sql line-numbers-mode" data-highlighter="prismjs" data-ext="sql" data-title="sql"><pre v-pre class="language-sql"><code><span class="line"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> <span class="token keyword">session</span></span>
<span class="line"><span class="token punctuation">(</span></span>
<span class="line">    id <span class="token keyword">CHAR</span><span class="token punctuation">(</span><span class="token number">40</span><span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span></span>
<span class="line">    expire <span class="token keyword">INTEGER</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token keyword">data</span> <span class="token keyword">BLOB</span></span>
<span class="line"><span class="token punctuation">)</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>其中 ‘BLOB’ 对应你选择的数据库管理系统的 BLOB-type 类型，以下一些常用数据库管理系统的 BLOB 类型：</p>
<ul>
<li>MySQL: LONGBLOB</li>
<li>PostgreSQL: BYTEA</li>
<li>MSSQL: BLOB</li>
</ul>
<blockquote>
<p>Note: 根据 php.ini 设置的 <code v-pre>session.hash_function</code>，你需要调整 <code v-pre>id</code> 列的长度，例如，如果 <code v-pre>session.hash_function=sha256</code>，应使用长度为 64 而不是 40 的 char 类型。</p>
</blockquote>
<p>或者，可以通过以下迁移完成：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>Migration</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">m170529_050554_create_table_session</span> <span class="token keyword">extends</span> <span class="token class-name">Migration</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">up</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">createTable</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'{{%session}}'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">char</span><span class="token punctuation">(</span><span class="token number">64</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'expire'</span> <span class="token operator">=></span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">integer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'data'</span> <span class="token operator">=></span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">binary</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">addPrimaryKey</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'pk-id'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'{{%session}}'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">down</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">dropTable</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'{{%session}}'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="flash-数据" tabindex="-1"><a class="header-anchor" href="#flash-数据"><span>Flash 数据</span></a></h3>
<p>Flash 数据是一种特别的 session 数据，它一旦在某个请求中设置后，只会在下次请求中有效，然后该数据就会自动被删除。常用于实现只需显示给终端用户一次的信息，如用户提交一个表单后显示确认信息。</p>
<p>可通过 <code v-pre>session</code> 应用组件设置或访问 <code v-pre>session</code>，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$session</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">session</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 请求 #1</span></span>
<span class="line"><span class="token comment">// 设置一个名为"postDeleted" flash 信息</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">setFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'postDeleted'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'You have successfully deleted your post.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 请求 #2</span></span>
<span class="line"><span class="token comment">// 显示名为"postDeleted" flash 信息</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token variable">$session</span><span class="token operator">-></span><span class="token function">getFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'postDeleted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 请求 #3</span></span>
<span class="line"><span class="token comment">// $result 为 false，因为flash信息已被自动删除</span></span>
<span class="line"><span class="token variable">$result</span> <span class="token operator">=</span> <span class="token variable">$session</span><span class="token operator">-></span><span class="token function">hasFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'postDeleted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>和普通 session 数据类似，可将任意数据存储为 flash 数据。</p>
<p>当调用 [[yii\web\Session::setFlash()]] 时，会自动覆盖相同名的已存在的任何数据，为将数据追加到已存在的相同名 flash 中，可改为调用 [[yii\web\Session::addFlash()]]。例如:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$session</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">session</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 请求 #1</span></span>
<span class="line"><span class="token comment">// 在名称为"alerts"的flash信息增加数据</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">addFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'alerts'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'You have successfully deleted your post.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">addFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'alerts'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'You have successfully added a new friend.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$session</span><span class="token operator">-></span><span class="token function">addFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'alerts'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'You are promoted.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 请求 #2</span></span>
<span class="line"><span class="token comment">// $alerts 为名为'alerts'的flash信息，为数组格式</span></span>
<span class="line"><span class="token variable">$alerts</span> <span class="token operator">=</span> <span class="token variable">$session</span><span class="token operator">-></span><span class="token function">getFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'alerts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Note: 不要在相同名称的 flash 数据中使用 [[yii\web\Session::setFlash()]] 的同时也使用 [[yii\web\Session::addFlash()]]，因为后一个方法会自动将 flash 信息转换为数组，以使新的 flash 数据可追加进来，因此，当你调用 [[yii\web\Session::getFlash()]] 时，会发现有时获取到一个数组，有时获取到一个字符串，取决于你调用这两个方法的顺序。</p>
</blockquote>
<blockquote>
<p>Tip: 要显示 Flash 消息，您可以通过以下方式使用 <code v-pre>yii\bootstrap\Alert</code> 小部件：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">echo</span> <span class="token class-name static-context">Alert</span><span class="token operator">::</span><span class="token function">widget</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">   <span class="token string single-quoted-string">'options'</span> <span class="token operator">=></span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'alert-info'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">   <span class="token string single-quoted-string">'body'</span> <span class="token operator">=></span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">session</span><span class="token operator">-></span><span class="token function">getFlash</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'postDeleted'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></blockquote>
<h2 id="cookies" tabindex="-1"><a class="header-anchor" href="#cookies"><span>Cookies</span></a></h2>
<p>Yii 使用 <code v-pre>yii\web\Cookie</code> 对象来代表每个 cookie，<code v-pre>yii\web\Request</code> 和 <code v-pre>yii\web\Response</code> 通过名为 cookies 的属性维护一个 cookie 集合，前者的 cookie 集合代表请求提交的 cookies，后者的 cookie 集合表示发送给用户的 cookies。</p>
<p>控制器是直接处理请求和响应的部分。因此，应当在控制器中读取和发送 cookie（译者注：意思在控制器中处理 cookie 是安全的）。</p>
<h3 id="读取-cookies" tabindex="-1"><a class="header-anchor" href="#读取-cookies"><span>读取 Cookies</span></a></h3>
<p>当前请求的 cookie 信息可通过如下代码获取：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 从 "request" 组件中获取 cookie 集合（yii\web\CookieCollection）</span></span>
<span class="line"><span class="token variable">$cookies</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">request</span><span class="token operator">-></span><span class="token property">cookies</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 获取名为 "language" cookie 的值，如果不存在，返回默认值 "en"</span></span>
<span class="line"><span class="token variable">$language</span> <span class="token operator">=</span> <span class="token variable">$cookies</span><span class="token operator">-></span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'en'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 另一种方式获取名为 "language" cookie 的值</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token variable">$cookie</span> <span class="token operator">=</span> <span class="token variable">$cookies</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token constant">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$language</span> <span class="token operator">=</span> <span class="token variable">$cookie</span><span class="token operator">-></span><span class="token property">value</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 可将 $cookies 当作数组使用</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$cookies</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$language</span> <span class="token operator">=</span> <span class="token variable">$cookies</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token operator">-></span><span class="token property">value</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 判断是否存在名为 "language" 的 cookie</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$cookies</span><span class="token operator">-></span><span class="token function">has</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$cookies</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">...</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="发送-cookies" tabindex="-1"><a class="header-anchor" href="#发送-cookies"><span>发送 Cookies</span></a></h3>
<p>可使用如下代码发送 cookie 到终端用户：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 从 "response" 组件中获取 cookie 集合（yii\web\CookieCollection）</span></span>
<span class="line"><span class="token variable">$cookies</span> <span class="token operator">=</span> <span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">response</span><span class="token operator">-></span><span class="token property">cookies</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 在要发送的响应中添加一个新的 cookie</span></span>
<span class="line"><span class="token variable">$cookies</span><span class="token operator">-></span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>Cookie</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'name'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'language'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'value'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'zh-CN'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 删除一个 cookie</span></span>
<span class="line"><span class="token variable">$cookies</span><span class="token operator">-></span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// 等同于以下删除代码</span></span>
<span class="line"><span class="token keyword">unset</span><span class="token punctuation">(</span><span class="token variable">$cookies</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'language'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>除了上述例子定义的 [[yii\web\Cookie::name]] 和 [[yii\web\Cookie::value]] 属性，<code v-pre>yii\web\Cookie</code> 类也定义了其他属性来实现 cookie 的各种信息，如 [[yii\web\Cookie::domain]]，[[yii\web\Cookie::expire]] 可配置这些属性到 cookie 中并添加到响应的 cookie 集合中。</p>
<blockquote>
<p>Note: 为安全起见 [[yii\web\Cookie::httpOnly]] 被设置为 true，这可减少客户端脚本访问受保护 cookie（如果浏览器支持）的风险，更多详情可阅读 <a href="https://owasp.org/www-community/HttpOnly" target="_blank" rel="noopener noreferrer">HttpOnly</a> 文章。</p>
</blockquote>
<h3 id="cookie-验证" tabindex="-1"><a class="header-anchor" href="#cookie-验证"><span>Cookie 验证</span></a></h3>
<p>在上两节中，当通过 <code v-pre>request</code> 和 <code v-pre>response</code> 组件读取和发送 cookie 时，你会喜欢扩展的 cookie 验证的保障安全功能，它能使 cookie 不被客户端修改。该功能通过给每个 cookie 签发一个哈希字符串来告知服务端 cookie 是否在客户端被修改，如果被修改，通过 <code v-pre>request</code> 组件的 [[yii\web\Request::cookies]] cookie 集合访问不到该 cookie。</p>
<blockquote>
<p>Note: Cookie 验证只保护 cookie 值被修改，如果一个 cookie 验证失败，仍然可以通过 <code v-pre>$_COOKIE</code> 来访问该 cookie，因为这是第三方库对未通过 cookie 验证自定义的操作方式。</p>
</blockquote>
<p>Cookie 验证默认启用，可以设置 [[yii\web\Request::enableCookieValidation]] 属性为 false 来禁用它，尽管如此，我们强烈建议启用它。</p>
<blockquote>
<p>Note: 直接通过 <code v-pre>$_COOKIE</code> 和 <code v-pre>setcookie()</code> 读取和发送的 Cookie 不会被验证。</p>
</blockquote>
<p>当使用 cookie 验证时，必须指定 [[yii\web\Request::cookieValidationKey]]，它是用来生成上述的哈希值，可通过在应用配置中配置 <code v-pre>request</code> 组件。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'components'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'request'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'cookieValidationKey'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'fill in a secret key here'</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Info: [[yii\web\Request::cookieValidationKey]] 属性对你的应用安全很重要，应只被你信任的人知晓，请不要将它放入版本控制中。</p>
</blockquote>
<blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


